#include "stdafx.h"
#include <vector>
#include <mshtml.h>
#include <ExDisp.h>
#include "javascript.h"
#include "h2http.h"
#include "win/win_app.h"

void __output(const wchar_t* pwmsg);

// ============================================================================
// JS_FUNCTION output(msg): output message to screen
// ============================================================================
Handle<Value> v8_output(const Arguments& args)
{
    if(args.Length() <= 0)
    {
        return Integer::New(0);
    }

    String::Utf8Value str(args[0]);

    STRING::unicode wstr(*str, true);
    if(*wstr)
    {
        __output(*wstr);
    }

    return Integer::New(0);
}


// ============================================================================
// JS_FUNCTION sleep(ms): sleep for a period
// ============================================================================
Handle<Value> v8_sleep(const Arguments& args)
{
    if(args.Length() <= 0)
    {
        return Integer::New(0);
    }

    int ms = args[0]->Uint32Value();

    Sleep(ms);

    return Integer::New(ms);
}


object_pool<h2http>     http_pool;


namespace HTTP{

// ============================================================================
// OBJECT   : http
// FUNCTION : http_constructor()
// COMMENT  : construction of http object
// USAGE    : new http();
// ============================================================================
Handle<Value> constructor(const Arguments& args)
{
	Handle<Object> object = args.This(); 
	HandleScope handle_scope;
    h2http* http = http_pool.new_object();
	object->SetInternalField(0, External::New(http)); 
	return object; 
} 

// ============================================================================
// OBJECT   : http
// FUNCTION : getter()
// COMMENT  : get properties of http object
// USAGE    : var attr = http.attr;
// ============================================================================
Handle<Value> getter(Local<String> property, const AccessorInfo& info)
{
    Handle<Object> obj = info.This ();
    h2http& http = *static_cast<h2http*> (Local<External>::Cast(obj->GetInternalField(0))->Value());

    String::AsciiValue name(property);
    if(!*name)
    {
        return Integer::New(0);
    }

    if(strcmp(*name, "cookie") == 0)
    {
        return String::New(http.get_cookie().c_str());
    }

    if(strcmp(*name, "host") == 0)
    {
        int ip = http.get_serv_ip();
        char tmpstr[256];
        sprintf_s(tmpstr,"%d.%d.%d.%d", ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF);
        return String::New(tmpstr);
    }

    if(strcmp(*name, "port") == 0)
    {
        return Integer::New(http.get_serv_port());
    }

    if(strcmp(*name, "header") == 0)
    {
        return String::New(http.make_header("GET", "hTTp://www.google.com.hk/index.html").c_str());
    }

    STRING::unicode wstr(http.get_property(*name).c_str());
    return String::New((uint16_t*)*wstr);
}

// ============================================================================
// OBJECT   : http
// FUNCTION : setter()
// COMMENT  : set properties of http object
// USAGE    : http.attr = value;
// ============================================================================
void setter(Local<String> property, Local<Value> value, const AccessorInfo& info)
{
    Handle<Object> obj = info.This ();
    h2http& http = *static_cast<h2http*> (Local<External>::Cast(obj->GetInternalField(0))->Value());

    String::Utf8Value utf8name(property);
    String::Utf8Value utf8val(value);

    if(!*utf8name || !*utf8val)
    {
        return;
    }

    STRING::ansi name(*utf8name);
    STRING::ansi val(*utf8val);


    if(strcmp(*name, "cookie") == 0)
    {
        http.set_cookie(*val);
        return;
    }

    if(strcmp(*name, "host") == 0)
    {
        http.set_serv_host(*val);
        return;
    }

    if(strcmp(*name, "port") == 0)
    {
        http.set_serv_port(value->Int32Value());
        return;
    }

    if(strcmp(*name, "header") == 0)
    {
        http.reset_header();
        http.set_header(*val);
        return;
    }


    //http.set_property(*name, *value);
}


// ============================================================================
// OBJECT   : http
// FUNCTION : http.send(method, url)
// COMMENT  : send HTTP request to server and receive the HTTP response
// USAGE    : http.send("GET", "http://www.test.com:8080/");
// ============================================================================
Handle<Value> send(const Arguments& args)
{
    Handle<Object> obj = args.This();
    h2http& http = *static_cast<h2http*> (Local<External>::Cast(obj->GetInternalField(0))->Value());

    if(args.Length() < 2)
    {
        return Integer::New(498);
    }

    String::Utf8Value method(args[0]);
    String::Utf8Value url(args[1]);

    return Integer::New(http.request(*method, *url));
}


// ============================================================================
// OBJECT   : http
// FUNCTION : http.get_cookie(method, url)
// COMMENT  : get one cookie property from current cookies
// USAGE    : http.get_cookie("SID");
// ============================================================================
Handle<Value> get_cookie(const Arguments& args)
{
    Handle<Object> obj = args.This();
    h2http& http = *static_cast<h2http*> (Local<External>::Cast(obj->GetInternalField(0))->Value());

    if(args.Length() < 1)
    {
        return String::New("");
    }

    String::Utf8Value name(args[0]);

    return String::New(http.get_cookie(*name).c_str());
}


// ============================================================================
// OBJECT   : http
// FUNCTION : iecookie(domain)
// COMMENT  : get current session cookies of IE
// USAGE    : http.iecookie("qq.com");
// ============================================================================
Handle<Value> iecookie(const Arguments& args)
{
    Handle<Object> obj = args.This();
    h2http& http = *static_cast<h2http*> (Local<External>::Cast(obj->GetInternalField(0))->Value());
    std::wstring cookies;

    if(!SUCCEEDED(CoInitialize(NULL)))
    {
        __output(L"Error: Failed to initialize COM.\n");
        return Integer::New(0);
    }

    IShellWindows * lpShellWindows  = NULL;
    if(!SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL, IID_IShellWindows, (PVOID*)&lpShellWindows)))
    {
        __output(L"Error: Failed to initialize IID_IShellWindows.\n");
        return Integer::New(0);
    }

    long nCount;
    lpShellWindows->get_Count(&nCount);

    for(int ie = 0; ie < nCount; ++ie)
    {
        VARIANT varIndex; varIndex.vt = VT_I4; varIndex.intVal = ie;
        IDispatch* lpDispatch = NULL;
        if(SUCCEEDED(lpShellWindows->Item(varIndex, &lpDispatch)))
        {
            IWebBrowser2* lpWebBrowser2;
            if(SUCCEEDED(lpDispatch->QueryInterface(IID_IWebBrowser2, (PVOID*)&lpWebBrowser2)))
            {
                IDispatch* lpDispatch;
                if(SUCCEEDED(lpWebBrowser2->get_Document(&lpDispatch)))
                {
                    IHTMLDocument2* lpDocument2;
                    if(SUCCEEDED(lpDispatch->QueryInterface(IID_IHTMLDocument2, (PVOID*)&lpDocument2)))
                    {
                        BSTR bstr;
                        if(SUCCEEDED(lpDocument2->get_URL(&bstr)))
                        {
                            STRING::ansi url(bstr);
                            SysFreeString(bstr);
                            std::string host;
                            h2http::parse_url(*url, &host);

                            for(int i = 0; i < args.Length(); ++i)
                            {
                                String::AsciiValue arg(args[i]);
                                if(0 == _stricmp(host.c_str(), *arg) && SUCCEEDED(lpDocument2->get_cookie(&bstr)))
                                {
                                    cookies += bstr;
                                    if(cookies.length() > 0 && cookies[cookies.length()-1] != L';') cookies += L";";
                                    SysFreeString(bstr);
                                }
                            }
                        }
                        lpDocument2->Release();
                    }
                    lpDispatch->Release();
                }
                lpWebBrowser2->Release();
            }
            lpDispatch->Release();
        }
    }


    lpShellWindows->Release();

    CoUninitialize();

    return String::New((const uint16_t*)cookies.c_str());
}

}//namespace HTTP



void register_objects(Handle<ObjectTemplate>& templ)
{
    Handle<FunctionTemplate> httptempl = FunctionTemplate::New(HTTP::constructor);
    httptempl->SetClassName(String::New("http"));
    templ->Set(v8::String::New("http"), httptempl);

    Handle<ObjectTemplate> proto = httptempl->PrototypeTemplate();
    proto->Set("send", FunctionTemplate::New(HTTP::send));
    proto->Set("iecookie", FunctionTemplate::New(HTTP::iecookie));
    proto->Set("get_cookie", FunctionTemplate::New(HTTP::get_cookie));
    Handle<ObjectTemplate> http_inst = httptempl->InstanceTemplate();
    http_inst->SetInternalFieldCount(1);
    http_inst->SetAccessor(String::New("cookie"), HTTP::getter, HTTP::setter);
    http_inst->SetAccessor(String::New("host"), HTTP::getter, HTTP::setter);
    http_inst->SetAccessor(String::New("port"), HTTP::getter, HTTP::setter);
    http_inst->SetAccessor(String::New("header"),HTTP::getter, HTTP::setter);
    http_inst->SetAccessor(String::New("request"), HTTP::getter, HTTP::setter);
    http_inst->SetAccessor(String::New("response"), HTTP::getter, HTTP::setter);
    http_inst->SetAccessor(String::New("body"), HTTP::getter, HTTP::setter);
    http_inst->SetAccessor(String::New("info"), HTTP::getter, HTTP::setter);

    templ->Set(String::New("output"), FunctionTemplate::New(v8_output));
    templ->Set(String::New("sleep"), FunctionTemplate::New(v8_sleep));
}